<template>
  <div>
    <el-table :data="filterTable()" style="width: 100%" border height="500">
      <el-table-column type="expand">
        <template slot-scope="props"> 
<pre>{{ showStack(props.row.threadStack) }}</pre>
        </template>
      </el-table-column>
      <el-table-column prop="machine" label="机器名称" width="180" ></el-table-column>
      <el-table-column prop="jvm" label="JVM实例" width="180"></el-table-column>
      <el-table-column prop="threadName" label="线程名称"></el-table-column>
      <el-table-column prop="threadId" label="线程ID"></el-table-column>
      <el-table-column prop="threadStatus" label="线程状态"></el-table-column>
      <el-table-column prop="cpuTime" label="CPU时间" sortable></el-table-column>
      <el-table-column prop="userTime" label="代码时间" sortable></el-table-column>
      <el-table-column prop="byteAllocated" label="内存空间"></el-table-column>
      <el-table-column prop="" label="堆栈" >
        <template slot="header" slot-scope="scope">
          <el-input v-model="searchKey" v-on:change="enter" ></el-input>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import stackContent from '@/mock/stackContent.json';
export default {
  name:'thread-stack',
  data() {
    return {
      searchKey:"",
      tableData: []
    }
  },
  created:function() {
    this.tableData = this.getStackContent();
  },
  methods:{
    getStackContent() {
      //console.log(stackContent['stack']);
      const stackLine = stackContent['stack'].split("\r\n");
      console.log(stackLine);
      const stackData = [];
      //先知道与几个节点
      //Method Server Stack Traces
      for(let i=0;i<stackLine.length;i++) {
        let line = stackLine[i];
        //一个节点
        if(line.includes('Method Server Stack Traces')) {
          let result = this.getMachineInfo(stackLine,i);
          //console.info(result);
          stackData.push(result);
        }
      }

      let stack = [];
      for(let idx in stackData){
        let machine = stackData[idx];
        for(let machineId in machine) {
          let methods = machine[machineId];
          for(let methodId in methods) {
            let stacktracks = methods[methodId]
            stacktracks.forEach(e=>{
              let row = { machine:machineId };
              row['jvm'] = methodId;
              // row['threadName'] = e.threadName;
              // row['threadId'] = e.threadId;
              // row['threadStatus'] = e.threadStatus;
              // row['cpuTime'] = e.cpuTime;
              // row['userTime'] = e.userTime;
              // row['byteAllocated'] = e.byteAllocated;
              // row['threadStack'] = e.threadStack;
              row={...row,...e};
              stack.push(row);
            })
          }
        }
      }
      console.log(stack);
      return stack;
    },
    /**
     * 获取每个节点的堆栈信息
     */
    getMachineInfo(stackLine,startId) {
      let result = {};
      let machineId = null;
      for(let x=startId+1;x<stackLine.length;x++) {
        let line = stackLine[x];
        //Server Manager: 获取节点名称
        if(line.includes("Server Manager: ")) {
          machineId = line.substring("Server Manager: ".length)
          result[machineId]={};
        }else if(line.includes("Method Server: ")) {
          //获取MethodServer的进程实例Id
          let msId = line.substring("Method Server: ".length)
          console.log('machineId:',machineId,'msId:',msId,'line:',line,"result:",result[machineId]);
          result[machineId][msId] = this.getMethodServerInfo(stackLine,x);
        }
        //如果再次读取从'Method Server Stack Traces'表示读取下一个节点了
        if(line.includes('Method Server Stack Traces')) {
          return result;
        }
      }
      return result;
    },
    /**
     * 获取MethodServer的堆栈信息
     */
    getMethodServerInfo(stackLine,startId) {
      let threadInfos = []
      let threadInfo = {};
      let threadLine = [];
      let pushLine = false;
      for(let x=startId+1;x<stackLine.length;x++) {
        let line = stackLine[x];
        //"JMX client heartbeat 68" Id=1231 daemon prio=5 TIMED_WAITING
        if(line.startsWith("\"")) {
          threadInfo = {...this.getTheadInfo(line)};
          pushLine = true;
        }else if(line.includes("CPU nanos:")) {
          //CPU nanos: 15600100; User nanos: 0; Bytes allocated: 131656
          line.split("; ").forEach(e=>{
            if(e.includes("CPU nanos:")) {
              threadInfo['cpuTime'] = parseInt(e.substring(e.lastIndexOf(":")+1));
            }else if(e.includes('User nanos:')) {
              threadInfo['userTime'] = parseInt(e.substring(e.lastIndexOf(":")+1));
            }else if(e.includes('Bytes allocated:')) {
              threadInfo['byteAllocated'] = parseInt(e.substring(e.lastIndexOf(":")+1));
            }
          })
          threadInfos.push(threadInfo);
        }else if(line.includes("Method Server: ")){
          //再次读取Method Server表示这个MethodServer已经读完了
          break;
        }else if(line==" ") {//每个线程堆栈末尾都有空行
          threadInfo['threadStack'] = threadLine.join("\r\n");
          threadLine=[];
          pushLine = false;
        }
        if(pushLine) {
          threadLine.push(line);
        }
      }
      return threadInfos;
    },
    /**
     * 获取线程属性
     * "ajp-bio-8010-exec-1" Id=198 daemon prio=5 RUNNABLE (in native)
     */
    getTheadInfo(thread) {
      let result = {};
      let spId = thread.lastIndexOf('"');
      result['threadName'] = thread.substring(1,spId);
      let splits = thread.substring(spId+1).split(" ");
      for(let i=1;i<splits.length;i++) {
        let split = splits[i]
        if(split.startsWith("Id=")) {
          result['threadId'] = split.substring(3);
        }else if(split=='RUNNABLE'
            ||split=='TIMED_WAITING'
            ||split=='BLOECKED'
            ||split=='WAITING') {
          result['threadStatus'] = split;
        }
      }
      return result;
    },
    filterTable() {
      if(this.searchKey.length>0) {
        return this.tableData.filter(e=>e.threadStack.includes(this.searchKey))
      }else{
        return this.tableData;
      }
    },
    enter:function() {
      if(this.searchKey.length>0) {
        return this.tableData.filter(e=>e.threadStack.includes(this.searchKey))
      }else{
        return this.tableData;
      }
    },
    showStack:function(stack) {
      const line = stack.split("\r\n");
      //屏蔽最后一次出现关键词之后的行数
      let maxNum = -1;
      let searchKey = this.searchKey;
      if(searchKey.length>0) {
        line.forEach((e,idx,arr) => {
          if(e.includes(searchKey)){
            maxNum = idx;
          }
        });
        if(maxNum!=-1) {
          return line.slice(0,maxNum+1).join("\r\n");
        }
      }
      return stack;
    }
  }
}
</script>

<style>

</style>